package com.justech.mes.v2.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.justech.mes.dto.EquipmentType;
import com.justech.mes.dto.InsertMesEquipConfigure;
import com.justech.mes.utils.DateUtil;
import com.justech.mes.utils.excel.ExcelUtilsForSheet;
import com.justech.mes.v2.dao.MesEquipRepository;
import com.justech.mes.v2.dao.repository.MesEquipBasicRepository;
import com.justech.mes.v2.dao.repository.MesEquipDetailRepository;
import com.justech.mes.v2.dao.repository.MesV2EquipmentConfigureBasicRepository;
import com.justech.mes.v2.dao.repository.MesV2EquipmentConfigureDetailRepository;
import com.justech.mes.v2.model.*;
import com.justech.mes.v2.model.vo.EquipmentVo;
import com.justech.mes.v2.service.MesEquipCollectV2Service;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.text.DecimalFormat;
import java.util.*;

/**
 * @author lin.jia
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class MesEquipCollectV2ServiceImpl implements MesEquipCollectV2Service {
    @Autowired
    private MesEquipRepository mesEquipRepository;

    @Autowired
    MesEquipBasicRepository basicRepository;

    @Autowired
    MesEquipDetailRepository detailRepository;

    @Autowired
    MesV2EquipmentConfigureBasicRepository configureBasicRepository;

    @Autowired
    MesV2EquipmentConfigureDetailRepository configureDetailRepository;

    @Override
    public Object add(EquipmentVo equipmentVo) {
        MesV2EquipmentConfigureBasic byMachineNameAndDataType = configureBasicRepository.findByMachineNameAndDataType(equipmentVo.getEquipmentName(), equipmentVo.getDataType());
        if (!"1".equals(equipmentVo.getDataType())) {
            if (byMachineNameAndDataType != null) {
                //有配置的情况下,查看是否有配置项
                if (configureDetailRepository.countByToleranceNameAndBasicId(equipmentVo.getData().get(0), byMachineNameAndDataType.getId()) < 1) {
                    return "@NoConfiguration";
                }
                //没有配置项
            } else {
                return "@NoMachineInformation";
            }
        }
        //查询是否有机台信息
        //删除没完成的数据
        final int ok = 1;
        String res = "@OK";
        //删除固定表未完成的信息
        List<MesEquipBasic> deleteList = basicRepository.findAllByEquipmentNameAndIsOkNotAndSnAndDataTypeNot(equipmentVo.getEquipmentName(), ok, equipmentVo.getSn(), equipmentVo.getDataType());
        if (!deleteList.isEmpty()) {
            deleteList.forEach(item -> {
                basicRepository.delete(item);
                detailRepository.deleteByBasicId(item.getId());
            });
        }
        //删除非固定表头
        List<MesEquip> deleteList2 = mesEquipRepository.findByEquipmentNameAndSnAndDataTypeNotAndIsOkNot(equipmentVo.getEquipmentName(), equipmentVo.getSn(), equipmentVo.getDataType(), ok);
        if (!deleteList2.isEmpty()) {
            deleteList2.forEach(item -> mesEquipRepository.delete(item));
        }
        //删除动态表未完成的逻辑
        //IO走下面的逻辑
        if (!"1".equals(equipmentVo.getDataType())) {
            List<String> data = equipmentVo.getData();
            MesEquip mesEquip = new MesEquip();
            BeanUtil.copyProperties(equipmentVo, mesEquip);
            mesEquip.setCreateBy("机台发送");
            mesEquip.setCreateTime(new Date());
            mesEquip.setUpdateBy("机台发送");
            mesEquip.setUpdateTime(new Date());
            mesEquip.setTitle(data.get(0));
            Double value = Double.valueOf(new DecimalFormat("0.000").format(Double.parseDouble(data.get(1))));
            mesEquip.setTitleData(value);
            //判断是否是重复发送的数据,重复发送的数据,采用最新的数据
            List<MesEquip> notRepeat = mesEquipRepository.findFirstByEquipmentNameAndSnAndIndexAndDataTypeAndIsRepeatAndTitle(mesEquip.getEquipmentName(), mesEquip.getSn(), mesEquip.getIndex(), mesEquip.getDataType(), 0, mesEquip.getTitle());
            if (!notRepeat.isEmpty()) {
                notRepeat.forEach(e -> {
                    e.setIsRepeat(1);
                    e.setUpdateBy("机台发送");
                    e.setUpdateTime(new Date());
                    mesEquipRepository.save(e);
                });
            }
            if (mesEquip.getIndex() == mesEquip.getLength()) {
                //判断结果,判定方法，最大减去最小的偏差是否满足公差;
                List<Double> tolerances = mesEquipRepository.findTolerances(mesEquip.getEquipmentName(), mesEquip.getDataType(), mesEquip.getTitle(), mesEquip.getIndex());
                tolerances.add(mesEquip.getTitleData());
                Double tolerance = (Collections.max(tolerances)) - (Collections.min(tolerances));
                String toleranceData = configureDetailRepository.findToleranceData(byMachineNameAndDataType.getId(), mesEquip.getTitle());
                res = Double.valueOf(new DecimalFormat("0.000").format(Double.parseDouble(toleranceData))) >= tolerance ? "@Pass" : "@Fail";
                List<MesEquip> all = mesEquipRepository.findByEquipmentNameAndSnAndDataTypeAndTitle(mesEquip.getEquipmentName(), mesEquip.getSn(), mesEquip.getDataType(), mesEquip.getTitle());
                if (!all.isEmpty()) {
                    mesEquip.setIsOk(1);
                    mesEquipRepository.updateIsOkV2(mesEquip.getEquipmentName(), mesEquip.getSn(), mesEquip.getDataType(), mesEquip.getTitle());
                }
            }
            mesEquip.setIsRepeat(0);
            mesEquipRepository.save(mesEquip);
            return res;
        } else {
            //类型还有设备号，
            MesEquipBasic basic = new MesEquipBasic();
            BeanUtil.copyProperties(equipmentVo, basic);
            List<MesEquipBasic> notRepeat = basicRepository.findByEquipmentNameAndSnAndDataTypeAndIndexAndIsRepeat(basic.getEquipmentName(), basic.getSn(), basic.getDataType(), basic.getIndex(), 0);
            if (!notRepeat.isEmpty()) {
                notRepeat.forEach(e -> {
                    e.setIsRepeat(1);
                    e.setUpdateBy("机台发送");
                    e.setUpdateTime(new Date());
                    e.setIsOk(e.getIsOk());
                    basicRepository.save(e);
                });
            }
            basic.setIsRepeat(0);
            //更新状态
            if (basic.getIndex() == basic.getLength()) {
                res = "OK".equals(equipmentVo.getData().get(2)) ? "@Pass" : "@Fail";
                //完成了,判定，并且把之前发送的都标记完成
                basic.setIsOk(1);
                List<MesEquipBasic> all = basicRepository.findAllByEquipmentNameAndSnAndDataType(basic.getEquipmentName(), basic.getSn(), basic.getDataType());
                if (!all.isEmpty()) {
                    basicRepository.updateIsOk(basic.getEquipmentName(), basic.getSn(), basic.getDataType());
                }
            }
            Long basicId = generateId();
            basic.setId(basicId);
            basic.setCreateTime(DateUtil.now());
            basicRepository.save(basic);
            List<String> list = equipmentVo.getData();
            for (int i = 0; i < list.size(); i++) {
                MesEquipDetail detail = new MesEquipDetail();
                detail.setBasicId(basicId);
                detail.setData(list.get(i));
                detail.setUpdateBy(String.valueOf(i));
                detail.setCreateTime(DateUtil.now());
                detailRepository.save(detail);
            }
            return res;
        }
    }

    private Long generateId() {
        int randomRound = 100000;
        return System.currentTimeMillis() + new Random().nextInt(randomRound);
    }

    @Override
    public void exportExcel(String equipmentName, String sn) throws Exception {
        ExcelUtilsForSheet eeu = new ExcelUtilsForSheet();
        HSSFWorkbook sheets = new HSSFWorkbook();
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletResponse response = servletRequestAttributes.getResponse();
        //表头
        String[] io = {"设备名称", "机台SN", "数据类型", "当前计数", "数据总量", "开始时间", "结束时间", "结果"};
        //IO
        getFixedWatchHead(0, io, sheets, EquipmentType.IO, equipmentName, sn, eeu, byteArrayOutputStream);
        //轴精度
        getUnstableHeaders(1, EquipmentType.AXIS_ACCURACY, equipmentName, sn, eeu, sheets, byteArrayOutputStream);
        //静态数据
        getUnstableHeaders(2, EquipmentType.STATIC_DATA, equipmentName, sn, eeu, sheets, byteArrayOutputStream);
        //动态数据
        getUnstableHeaders(3, EquipmentType.DYNAMIC_DATA, equipmentName, sn, eeu, sheets, byteArrayOutputStream);
        //CAMERA_CALIBRATION
        getUnstableHeaders(4, EquipmentType.CAMERA_CALIBRATION, equipmentName, sn, eeu, sheets, byteArrayOutputStream);
        exportData(byteArrayOutputStream, response, sheets, equipmentName, sn);
    }

    @Override
    public void addMesEquipConfigure(InsertMesEquipConfigure model) {
        if (configureBasicRepository.countByMachineNameAndDataType(model.getMachineName(), model.getDataType()) >= 1) {
            throw new IllegalArgumentException("机台名称和数据类型已经存在");
        }
        MesV2EquipmentConfigureBasic basic = new MesV2EquipmentConfigureBasic();
        BeanUtils.copyProperties(model, basic);
        basic.setCreateBy("TCP机台发送");
        basic.setCreateTime(new Date());
        configureBasicRepository.save(basic);
        List<HashMap<String, String>> domains = model.getDomains();
        domains.forEach(item -> {
            MesV2EquipmentConfigureDetail detail = new MesV2EquipmentConfigureDetail();
            detail.setToleranceName(item.get("parameterName"));
            detail.setBasicId(basic.getId());
            try {
                new DecimalFormat("0.000").format(Double.parseDouble(item.get("tolerance")));
            } catch (NumberFormatException e) {
                throw new RuntimeException("参数格式错误，请填写数值");
            }
            detail.setToleranceData(item.get("tolerance"));
            detail.setCreateBy("TCP机台发送");
            detail.setCreateTime(new Date());
            configureDetailRepository.save(detail);
        });
    }

    @Override
    public void updateMesEquipConfigure(InsertMesEquipConfigure model) {
        MesV2EquipmentConfigureBasic basic = new MesV2EquipmentConfigureBasic();
        BeanUtils.copyProperties(model, basic);
        basic.setCreateBy("TCP机台发送");
        basic.setCreateTime(new Date());
        configureBasicRepository.save(basic);
        configureDetailRepository.deleteByBasicId(basic.getId());
        List<HashMap<String, String>> domains = model.getDomains();
        domains.forEach(item -> {
            MesV2EquipmentConfigureDetail detail = new MesV2EquipmentConfigureDetail();
            detail.setToleranceName(item.get("parameterName"));
            detail.setBasicId(basic.getId());
            try {
                new DecimalFormat("0.000").format(Double.parseDouble(item.get("tolerance")));
            } catch (NumberFormatException e) {
                throw new RuntimeException("参数格式错误，请填写数值");
            }
            detail.setToleranceData(item.get("tolerance"));
            detail.setCreateBy("TCP机台发送");
            detail.setCreateTime(new Date());
            configureDetailRepository.save(detail);
        });
    }

    @Override
    public InsertMesEquipConfigure findById(Long id) {
        InsertMesEquipConfigure mesEquipConfigure = new InsertMesEquipConfigure();
        Optional<MesV2EquipmentConfigureBasic> byId = configureBasicRepository.findById(id);
        MesV2EquipmentConfigureBasic mesV2EquipmentConfigureBasic = byId.get();
        BeanUtils.copyProperties(mesV2EquipmentConfigureBasic, mesEquipConfigure);
        List<MesV2EquipmentConfigureDetail> byBasicIdInMap = configureDetailRepository.findByBasicIdInMap(id);

        List<HashMap<String, String>> hashMaps = new ArrayList<HashMap<String, String>>();
        for (MesV2EquipmentConfigureDetail configureDetail : byBasicIdInMap) {
            HashMap<String, String> map = new HashMap<String, String>();
            map.put("parameterName", configureDetail.getToleranceName());
            map.put("tolerance", configureDetail.getToleranceData());
            hashMaps.add(map);
        }
        mesEquipConfigure.setDomains(hashMaps);
        return mesEquipConfigure;
    }

    /**
     * 不固定表头
     *
     * @param dataType
     * @param equipmentName
     * @param sn
     * @param eeu
     * @param sheets
     * @param byteArrayOutputStream
     * @throws Exception
     */
    private void getUnstableHeaders(Integer num, String dataType, String equipmentName, String sn, ExcelUtilsForSheet eeu, HSSFWorkbook sheets, ByteArrayOutputStream byteArrayOutputStream) throws Exception {
        List<MesEquip> staticDataList = mesEquipRepository.findByEquipmentNameAndSnAndDataType(equipmentName, sn, dataType);
        String[] headers = {"设备名称", "机台SN", "数据类型", "当前计数", "数据总量"};
        GetTitleAndData getTitleAndData = new GetTitleAndData(dataType, equipmentName, sn, staticDataList, headers).invoke();
        headers = getTitleAndData.getHeaders();
        List<List<String>> resultData = getTitleAndData.getResultData();
        String title = getTitleAndData.getTitle();
        eeu.exportExcel(sheets, num, title, headers, resultData, byteArrayOutputStream);
    }

    /**
     * 固定表头的
     *
     * @param sheetNum
     * @param head
     * @param sheets
     * @param type
     * @param equipmentName
     * @param sn
     * @param eeu
     * @param byteArrayOutputStream
     * @throws Exception
     */
    private void getFixedWatchHead(Integer sheetNum, String[] head, HSSFWorkbook sheets, String type, String equipmentName, String sn, ExcelUtilsForSheet eeu, ByteArrayOutputStream byteArrayOutputStream) throws
            Exception {
        List<MesEquipBasic> data = getData(type, equipmentName, sn);
        List<List<String>> ioDataForSheet = new ArrayList<List<String>>();
        getData(data, ioDataForSheet);
        String title = "";
        title = setTitle(type, title);
        eeu.exportExcel(sheets, sheetNum, title, head, ioDataForSheet, byteArrayOutputStream);
    }

    /**
     * 设置sheet名字
     *
     * @param type
     * @param title
     * @return
     */
    private String setTitle(String type, String title) {
        switch (type) {
            case "1":
                title = "IO点检";
                break;
            case "2":
                title = "轴精度";
                break;
            case "3":
                title = "静态数据";
                break;
            case "4":
                title = "动态数据";
                break;
            case "5":
                title = "相机标定";
                break;
            case "6":
                title = "CPK";
                break;
            case "7":
                title = "GRR";
                break;
            case "8":
                title = "相关性";
                break;
            case "9":
                title = "压力标定";
                break;
            default:
        }
        return title;
    }

    /**
     * 分类提取数据
     *
     * @param dataType
     * @param equipmentName
     * @param sn
     * @return
     */
    private List<MesEquipBasic> getData(String dataType, String equipmentName, String sn) {
        return basicRepository.findAllByDataTypeAndEquipmentNameAndSnAndIsRepeatOrderByIndex(dataType, equipmentName, sn, 0);
    }

    /**
     * 创建dataForSheet数据
     *
     * @param Data
     * @param DataForSheet
     */
    private void getData(List<MesEquipBasic> Data, List<List<String>> DataForSheet) {
        for (MesEquipBasic datum : Data) {
            List rowData = new ArrayList();
            List<MesEquipDetail> allByBasicId = detailRepository.findAllByBasicIdOrderByUpdateBy(datum.getId());
            setInfo(datum, rowData);
            for (MesEquipDetail equipDetail : allByBasicId) {
                rowData.add(equipDetail.getData());
            }
            DataForSheet.add(rowData);
        }
    }

    /**
     * 塞入基础数据
     *
     * @param datum
     * @param rowData
     */
    private void setInfo(MesEquipBasic datum, List rowData) {
        rowData.add(datum.getEquipmentName());
        rowData.add(datum.getSn());
        rowData.add(getDataType(datum));
        rowData.add(String.valueOf(datum.getIndex()));
        rowData.add(String.valueOf(datum.getLength()));
    }

    /**
     * 设置数据类型中文
     *
     * @param datum
     */
    private String getDataType(MesEquipBasic datum) {
        String title = "";
        switch (datum.getDataType()) {
            case "1":
                title = "IO点检";
                break;
            case "2":
                title = "轴精度";
                break;
            case "3":
                title = "静态数据";
                break;
            case "4":
                title = "动态数据";
                break;
            case "5":
                title = "相机标定";
                break;
            case "6":
                datum.setDataType("CPK");
                title = "轴精度";
                break;
            case "7":
                title = "GRR";
                break;
            case "8":
                title = "相关性";

                break;
            case "9":
                title = "压力标定";
                break;
            default:
        }
        return title;
    }

    /**
     * 往字符串数组追加新数据
     *
     * @param arr
     * @param str
     * @return
     */
    private static String[] insert(String[] arr, String str) {
        //获取数组长度
        int size = arr.length;
        //新建临时字符串数组，在原来基础上长度加一
        String[] tmp = new String[size + 1];
        //先遍历将原来的字符串数组数据添加到临时字符串数组
        for (int i = 0; i < size; i++) {
            tmp[i] = arr[i];
        }
        //在最后添加上需要追加的数据
        tmp[size] = str;
        //返回拼接完成的字符串数组
        return tmp;
    }

    /**
     * 通过流写出
     *
     * @param byteArrayOutputStream
     * @param response
     * @param sheets
     * @throws IOException
     */
    private void exportData(ByteArrayOutputStream byteArrayOutputStream, HttpServletResponse response, HSSFWorkbook sheets, String equipmentName, String sn) throws IOException {
        sheets.write(byteArrayOutputStream);
        String suffix = ".xls";
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        response.setHeader("Content-Disposition",
                "attachment;filename=" + new String(("设备对接信息" + "--" + equipmentName + "--" + sn + suffix).getBytes(), "iso-8859-1"));
        OutputStream outputStream = response.getOutputStream();
        outputStream.write(byteArrayOutputStream.toByteArray());
        outputStream.close();
        byteArrayOutputStream.close();
    }

    /**
     * 内部类处理不固定表头逻辑
     */
    private class GetTitleAndData {
        private String dataType;
        private String equipmentName;
        private String sn;
        private List<MesEquip> staticDataList;
        private String[] headers;
        private List<List<String>> resultData;
        private String title;

        private GetTitleAndData(String dataType, String equipmentName, String sn, List<MesEquip> staticDataList, String... headers) {
            this.dataType = dataType;
            this.equipmentName = equipmentName;
            this.sn = sn;
            this.staticDataList = staticDataList;
            this.headers = headers;
        }

        private String[] getHeaders() {
            return headers;
        }

        private List<List<String>> getResultData() {
            return resultData;
        }

        private String getTitle() {
            return title;
        }

        private GetTitleAndData invoke() {
            List<String> titleList = mesEquipRepository.findTitle(equipmentName, sn, dataType);
            for (String s : titleList) {
                headers = insert(headers, s);
            }
            if (!staticDataList.isEmpty()) {
                int length = staticDataList.get(0).getLength();
                //动态表头的数据
                resultData = new ArrayList<List<String>>();
                for (int i = 1; i <= length; i++) {
                    List rowData = new ArrayList();
                    MesEquip datum = mesEquipRepository.findFirstByEquipmentNameAndSnAndIndexAndDataTypeAndIsRepeat(equipmentName, sn, i, dataType, 0);
                    if (datum != null) {
                        rowData.add(datum.getEquipmentName());
                        rowData.add(datum.getSn());
                        switch (datum.getDataType()) {
                            case "1":
                                rowData.add("IO点检");
                                break;
                            case "2":
                                rowData.add("轴精度");
                                break;
                            case "3":
                                rowData.add("静态数据");
                                break;
                            case "4":
                                rowData.add("动态数据");
                                break;
                            case "5":
                                rowData.add("相机标定");
                                break;
                            case "6":
                                rowData.add("CPK");
                                break;
                            case "7":
                                rowData.add("GRR");
                                break;
                            case "8":
                                rowData.add("相关性");
                                break;
                            case "9":
                                rowData.add("压力标定");
                                break;
                            default:
                        }
                        rowData.add(String.valueOf(datum.getIndex()));
                        rowData.add(String.valueOf(datum.getLength()));
                        for (String s : titleList) {
                            Double data = mesEquipRepository.findData(equipmentName, sn, dataType, s, i, 0);
                            rowData.add(data != null ? String.valueOf(data) : "");
                        }
                        resultData.add(rowData);
                    }
                }
            }
            title = "";
            title = setTitle(dataType, title);
            return this;
        }
    }
}
